/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package argtrust.argsys;

import argtrust.trustnet.Agent;
import java.util.Map;
import java.util.LinkedHashMap;
import java.lang.Integer;
import argtrust.util.Pair;
import argtrust.measure.IMeasureOperators;
import argtrust.belief.IndirectBelief;

/**
 *
 * TODO: Double-check with the following papers:
 * 1) H. Jakobovits and D. Vermeir. Robust semantics for argumentation frameworks. Journal of
 * logic and computation, 9(2):215–261, 1999.
 * 2) S. Modgil and M. Caminada. Argumentation in Artiﬁcial Intelligence, chapter Proof Theories
 * and Algorithms for Abstract Argumentation Frameworks, pages 105–129. Springer, 2009
 * @author Yuqing Tang
 */
public abstract class LabelSemantics implements IArgumentationSemantics {

    IBeliefArgumentSemantics mArgumentBeliefSemantics;
    ArgumentationSystem mArgSys;
    IMeasureOperators mBeliefComparator;
    Map<Pair<Agent, Argument>, LabelStatus> mArgumentStatusTable = new LinkedHashMap<Pair<Agent, Argument>, LabelStatus>();
    int mDiameter = 0;
    boolean mDoneSomeRefined;

    public IMeasureOperators getComparator() {
        return mBeliefComparator;
    }

    public LabelSemantics(ArgumentationSystem arg_sys,
            IBeliefArgumentSemantics arg_belief) {
        mArgSys = arg_sys;
        mArgumentBeliefSemantics = arg_belief;
        mBeliefComparator = arg_belief.getOperators();
    }

    void recordDiameter(int depth) {
        if (depth > mDiameter) {
            mDiameter = depth;
        }
    }

    /**
     * A single-round labeled semantics which will take any arguments in
     * a loop that can not be broken by an IN defeater as UNDEC.
     * TODO: DEBUG: There are IN defeaters sometimes are not picked up!
     * @param agent
     * @param argument
     * @return
     */
    protected LabelStatus computeStatus_(Agent agent, Argument argument, int depth) {
        Pair<Agent, Argument> key = new Pair<Agent, Argument>(agent, argument);

        LabelStatus res = mArgumentStatusTable.get(key);

        if (res == null) {
            //0: There is IN; 1: all OUT; 3: There is UNDEC/EXPLORING
            int children_status_summary = 1;
            ArguingNode arg_node = mArgSys.getArguingNode(argument);
            assert (arg_node != null);
            res = new LabelStatus();
            mArgumentStatusTable.put(key, res);
            if (arg_node.getArguingLinkList() == null) {
                //No defeaters
                res.setStatus(LabelStatus.IN);

            } else {
                res.setExploring();
                boolean is_all_children_final = true;

                for (ArguingLink arg_link : arg_node.getArguingLinkList()) {

                    if (!isEffective(agent, arg_link.getDefeat())) {
                        continue;
                    }

                    ArguingNode next_node = arg_link.getDefeaterNode();
                    LabelStatus defeater_status = computeStatus_(agent,
                            next_node.getArgument(),
                            depth + 1);
                    if (defeater_status.isIn() //                            && is_effective
                            ) {
                        //If any defeater is IN, I'am OUT:
                        children_status_summary = 0;
                        break;
                    } else if ((defeater_status.isUndec() || defeater_status.isExploring()) //                            && is_effective
                            ) {
                        if (!defeater_status.isFinal()) {
                            is_all_children_final = false;
                        }
                        children_status_summary = 3;
                    }
                }
                if (children_status_summary == 0) {
                    res.setStatus(LabelStatus.OUT);
                    res.setFinal();
                } else if (children_status_summary == 1) {
                    res.setStatus(LabelStatus.IN);
                    res.setFinal();
                } else //if (children_status_summary == 3)
                {
                    res.setStatus(LabelStatus.UNDEC);
                    if (is_all_children_final) {
                        res.setFinal();
                    }
                }
            }
        }
        recordDiameter(depth);
        return res;

    }

    protected LabelStatus refineStatus_(Agent agent, Argument argument) {
        Pair<Agent, Argument> key = new Pair<Agent, Argument>(agent, argument);
        LabelStatus res = mArgumentStatusTable.get(key);
        if (res != null) {
            if (!res.isUndec()
                    || res.isFinal()) {
                return res;
            }
            //0: There is IN; 1: all OUT; 3: There is UNDEC/EXPLORING
            int children_status_summary = 1;
            ArguingNode arg_node = mArgSys.getArguingNode(argument);
            assert (arg_node != null);

            if (arg_node.getArguingLinkList() == null) {
                //No defeaters
                res.setStatus(LabelStatus.IN);
                res.setFinal();
            } else {
                res.setExploring();
                boolean is_all_children_final = true;

                for (ArguingLink arg_link : arg_node.getArguingLinkList()) {

                    if (!isEffective(agent, arg_link.getDefeat())) {
                        continue;
                    }
                    ArguingNode next_node = arg_link.getDefeaterNode();
                    LabelStatus defeater_status =
                            refineStatus_(agent,
                            next_node.getArgument());
                    if (defeater_status.isIn()) {
                        //If any defeater is IN, I'am OUT:
                        children_status_summary = 0;
//                        res.setStatus(LabelStatus.OUT);
                        break;
                    } else if ((defeater_status.isUndec() || defeater_status.isExploring())) {
                        children_status_summary = 3;
                        if (!defeater_status.isFinal()) {
                            is_all_children_final = false;
                        }
//                        res.setStatus(LabelStatus.UNDEC);
                    }
                }
                if (children_status_summary == 0) {
                    res.setStatus(LabelStatus.OUT);
                    res.setFinal();
                } else if (children_status_summary == 1) {
                    res.setStatus(LabelStatus.IN);
                    res.setFinal();
                } else {
                    res.setStatus(LabelStatus.UNDEC);
                    if (is_all_children_final) {
                        res.setFinal();
                    }
                }
            }
            if (!res.isUndec() || res.isFinal()) {
                mDoneSomeRefined = true;
//                System.out.println("refine status of: ");
//                System.out.println(argument.getEndInferenceNode().toString());
//                System.out.println(" to: " + res.toString());
//                System.out.println("===");
            }
        }
        return res;
    }

    public boolean isEffective(Agent agent, Defeat defeat) {
//        System.out.println("defeatee belief: "
//                + mArgumentBeliefSemantics.believe(agent, defeat.getDefeatee()).toString());
//        System.out.println("defeater belief: "
//                + mArgumentBeliefSemantics.believe(agent, defeat.getDefeater()).toString());
        return !(mBeliefComparator.gt(
                mArgumentBeliefSemantics.believe(agent, defeat.getDefeatee()).getOpMeasure(),
                mArgumentBeliefSemantics.believe(agent, defeat.getDefeater()).getOpMeasure()));

    }

    /**
     * @param agent
     * @param argument
     * @return The status of argument w.r.t grounded semantics.
     */
    public ArgumentStatus computeComplete(Agent agent, Argument argument) {
        LabelStatus res = computeStatus_(agent, argument, 0);
        if (res.isFinal()) {
            return res;
        }
        for (int i = 0; i < mDiameter; ++i) {
            mDoneSomeRefined = false;
            res = refineStatus_(agent, argument);
            if (mDoneSomeRefined == false) {
                break;
            }
        }
        res.setFinal();
        return res;
    }
}
